﻿using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Platform.Storage;
using Avalonia.PropertyGrid.Services;
using PropertyModels.ComponentModel.DataAnnotations;

namespace Avalonia.PropertyGrid.Utils
{
    /// <summary>
    /// Class PathBrowserUtils.
    /// </summary>
    public static class PathBrowserUtils
    {
        /// <summary>
        /// Show open file dialog as an asynchronous operation.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="filters">The filters.</param>
        /// <param name="multipleSelection">if set to <c>true</c> [multiple selection].</param>
        /// <param name="title">The title.</param>
        /// <param name="filename">The filename.</param>
        /// <returns>A Task&lt;System.String&gt; representing the asynchronous operation.</returns>
        public static async Task<string[]?> ShowOpenFileDialogAsync(Window parent, string filters, bool multipleSelection = false, string? title = null, string? filename = null)
        {
            var files = await ShowDialogAsync(
                parent,
                multipleSelection ? PathBrowsableType.MultipleFiles : PathBrowsableType.File,
                false,
                title,
                filters,
                filename
                );

            return files;
        }

        /// <summary>
        /// Show open single file dialog as an asynchronous operation.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="filters">The filters.</param>
        /// <param name="title">The title.</param>
        /// <param name="filename">The filename.</param>
        /// <returns>A Task&lt;System.String&gt; representing the asynchronous operation.</returns>
        public static async Task<string?> ShowOpenSingleFileDialogAsync(Window parent, string filters, string? title = null, string? filename = null)
        {
            var files = await ShowDialogAsync(
                parent,
                PathBrowsableType.File,
                false,
                title,
                filters,
                filename
                );

            return files?.FirstOrDefault();
        }

        /// <summary>
        /// Show save file dialog as an asynchronous operation.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="filters">The filters.</param>
        /// <param name="title">The title.</param>
        /// <param name="filename">The filename.</param>
        /// <returns>A Task&lt;System.String&gt; representing the asynchronous operation.</returns>
        public static async Task<string?> ShowSaveFileDialogAsync(Window parent, string filters, string? title = null, string? filename = null)
        {
            var files = await ShowDialogAsync(
                parent,
                PathBrowsableType.File,
                true,
                title,
                filters,
                filename
                );

            return files?.FirstOrDefault();
        }

        /// <summary>
        /// Show open folder dialog as an asynchronous operation.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="title">The title.</param>
        /// <param name="filename">The filename.</param>
        /// <returns>A Task&lt;System.String&gt; representing the asynchronous operation.</returns>
        public static async Task<string?> ShowOpenFolderDialogAsync(Window parent, string? title = null, string? filename = null)
        {
            var files = await ShowDialogAsync(
                parent,
                PathBrowsableType.Directory,
                false,
                title,
                null,
                filename
                );

            return files?.FirstOrDefault();
        }

        private static IStorageProvider? GetStorageProvider(Window parentWindow) => TopLevel.GetTopLevel(parentWindow)?.StorageProvider;

        /// <summary>
        /// Show dialog as an asynchronous operation.
        /// </summary>
        /// <param name="parentWindow">The parent window.</param>
        /// <param name="type">The type.</param>
        /// <param name="saveMode">if set to <c>true</c> [save mode].</param>
        /// <param name="title">The title.</param>
        /// <param name="filters">The filters.</param>
        /// <param name="initFileName">Name of the initialize file.</param>
        /// <returns>A Task&lt;System.String[]&gt; representing the asynchronous operation.</returns>
        public static async Task<string[]?> ShowDialogAsync(Window parentWindow, PathBrowsableType type, bool saveMode, string? title, string? filters, string? initFileName)
        {
            var storageProvider = GetStorageProvider(parentWindow);

            if (storageProvider == null)
            {
                return null;
            }

            if (saveMode)
            {
                var options = new FilePickerSaveOptions
                {
                    Title = title ?? LocalizationService.Default["Please select a file"],
                    SuggestedFileName = initFileName,
                    FileTypeChoices = ConvertToFilterList(filters)
                };
                var storage = await storageProvider.SaveFilePickerAsync(options);

                return storage != null ? [storage.Path.LocalPath] : null;
            }

            if (type is PathBrowsableType.Directory or PathBrowsableType.MultipleDirectories)
            {
                var options = new FolderPickerOpenOptions();

                // If an InitFileName is specified, make that location available for opening.
                if (!string.IsNullOrEmpty(initFileName))
                {
                    var startFolder = await storageProvider.TryGetFolderFromPathAsync(initFileName);
                    options.SuggestedStartLocation = startFolder;
                }

                if (type == PathBrowsableType.MultipleDirectories)
                {
                    options.Title = title ?? LocalizationService.Default["Please select some folders"];
                    options.AllowMultiple = true;
                }
                else
                {
                    options.Title = title ?? LocalizationService.Default["Please select a folder"];
                    options.AllowMultiple = false;
                }

                var storage = await storageProvider.OpenFolderPickerAsync(options);
                return storage.Select(x => x.Path.LocalPath).ToArray();
            }

            if (type is PathBrowsableType.File or PathBrowsableType.MultipleFiles)
            {
                var options = new FilePickerOpenOptions();

                if (type == PathBrowsableType.MultipleFiles)
                {
                    options.Title = title ?? LocalizationService.Default["Please select some files"];
                    options.AllowMultiple = true;
                }
                else
                {
                    options.Title = title ?? LocalizationService.Default["Please select a file"];
                    options.AllowMultiple = false;
                }

                options.FileTypeFilter = ConvertToFilterList(filters);

                var storage = await storageProvider.OpenFilePickerAsync(options);

                return storage.Select(x => x.Path.LocalPath).ToArray();
            }

            return null;
        }

        /// <summary>
        /// Converts to filter list.
        /// </summary>
        /// <param name="filters">The filters.</param>
        /// <returns>List&lt;FileDialogFilter&gt;.</returns>
        public static List<FilePickerFileType> ConvertToFilterList(string? filters)
        {
            if (filters == null)
            {
                return [];
            }

            var list = new List<FilePickerFileType>();

            var results = filters.Split('|');

            for (var i = 0; i < results.Length / 2; ++i)
            {
                var name = results[(i * 2) + 0];
                var extensions = results[(i * 2) + 1];

                var filter = new FilePickerFileType(name.Trim())
                {
                    Patterns = extensions.Split(';').Select(x =>
                    {
                        var y = x.Trim();
                        return y;
                    }).ToList()
                };

                list.Add(filter);
            }

            return list;
        }

        /// <summary>
        /// Show path browser as an asynchronous operation.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="attribute">The attribute.</param>
        /// <returns>A Task&lt;System.String[]&gt; representing the asynchronous operation.</returns>
        public static async Task<string[]?> ShowPathBrowserAsync(Window parent, PathBrowsableAttribute attribute)
        {
            return await ShowDialogAsync(
                parent,
                attribute.Type,
                attribute.SaveMode,
                attribute.Title,
                attribute.Filters,
                attribute.InitialFileName
                );
        }
    }
}